import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'
import router from '@/router'
import store from '@/store'
import { clearStorage } from './localstoreage'
import { checkComplexDataw, removeStrInvaildSymb } from './toolkit'
import { baseUrl } from './baseUrl'
import { ant_design_vue } from './global/antDesignVue'
let { message, notification } = ant_design_vue

/**
 * 跳转登录页
 * 携带当前页面路由，以期在登录页面完成登录后返回当前页面
 */
const toLogin = () => { }

/**
 * 请求失败后的错误统一处理
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (status: number, other: string) => {
  if (other) {
  }
  // , other: string
  // 状态码判断
  switch (status) {
    case 302:
      message.error('接口重定向了！')
      break
    case 400:
      message.error('发出的请求有错误，服务器没有进行新建或修改数据的操作==>' + status)
      break
    // 401: 未登录
    // 未登录则跳转登录页面，并携带当前页面的路径
    // 在登录成功后返回当前页面，这一步需要在登录页操作。
    case 401: //重定向
      message.error('token:登录失效==>' + status + ':' + store.getters.GETROLE)
      // storage.remove(store.state.Roles)
      clearStorage()
      // storage.get(store.state.Roles) //意义不明
      // toLogin()
      break
    // 403 token过期
    // 清除token并跳转登录页
    case 403:
      message.error('登录过期,用户得到授权，但是访问是被禁止的==>' + status)
      // store.commit('token', null);
      setTimeout(() => {
        toLogin()
      }, 1000)
      break
    case 404:
      message.error('网络请求不存在==>' + status)
      break
    case 406:
      message.error('请求的格式不可得==>' + status)
      break
    case 408:
      message.error(' 请求超时！')
      break
    case 410:
      message.error('请求的资源被永久删除，且不会再得到的==>' + status)
      break
    case 422:
      message.error('当创建一个对象时，发生一个验证错误==>' + status)
      break
    case 500:
      message.error('服务器发生错误，请检查服务器==>' + status)
      break
    case 502:
      message.error('网关错误==>' + status)
      break
    case 503:
      message.error('服务不可用，服务器暂时过载或维护==>' + status)
      break
    case 504:
      message.error('网关超时==>' + status)
      break
    default:
      message.error('其他错误错误==>' + status)
  }
}

// 定义接口
interface PendingType {
  url?: string
  method?: Method|string
  params: any
  data: any
  cancel: any
}

// 取消重复请求
const pending: Array<PendingType> = []
const pendingWhiteList: string[] = ['/dd/getUserInfoByUserDDId', '/dd/ddGetAuth', 'dd/ddUploadFile']
const CancelToken = axios.CancelToken

// 移除重复请求
const removePending = (config: AxiosRequestConfig) => {
  for (const key in pending) {
    const item: number = +key
    const list: PendingType = pending[key]

    // 校验接口路由 接口名称 接口param参数以及data参数是否一致
    // 一致时移除重复请求
    if (config.url && pendingWhiteList.includes(config.url)) return
    if (
      list.url === config.url &&
      list.method === config.method &&
      JSON.stringify(list.params) === JSON.stringify(config.params) &&
      JSON.stringify(list.data) === JSON.stringify(config.data)
    ) {
      // 执行取消操作
      list.cancel('操作太频繁，请稍后再试' + list.url)
      // 从数组中移除记录
      pending.splice(item, 1)
    }

    // // 校验接口路由 接口名称 是否一致
    // // 一致时移除重复请求
    // if (list.url === config.url && list.method === config.method) {
    //   // 执行取消操作
    //   list.cancel('操作太频繁，请稍后再试');
    //   // 从数组中移除记录
    //   pending.splice(item, 1);
    // }
  }
}

/* 实例化请求配置 */
const instance = axios.create({
  // 请求时长
  timeout: 1000 * 60 * 10,
  // 请求的base地址 TODO:这块以后根据不同的模块调不同的api
  // baseURL: process.env.VUE_APP_BASE_API,
  baseURL: baseUrl,
  // 表示跨域请求时是否需要使用凭证
  withCredentials: false
})

/**
 * 请求拦截器
 * 每次请求前，如果存在token则在请求头中携带token
 */
instance.interceptors.request.use(
  (config ) => {
    removePending(config)
    config.cancelToken = new CancelToken((c: any) => {
      pending.push({
        url: config.url,
        method: config.method,
        params: config.params,
        data: config.data,
        cancel: c
      })
    })

    // 登录流程控制中，根据本地是否存在token判断用户的登录情况
    // 但是即使token存在，也有可能token是过期的，所以在每次的请求头中携带token
    // 后台根据携带的token判断用户的登录情况，并返回给我们对应的状态码
    // 而后我们可以在响应拦截器中，根据状态码进行一些统一的操作。

    // const token = getToken();
    // const userId = getUserId()
    // // localStorage.setItem('token', token);
    // if (config.url !== '/login') {
    //   if (token) {
    //     if (config.headers) {
    //       config.headers.token = token
    //       config.headers.platformuserid = userId

    //     } else {
    //       config.headers = {
    //         token
    //       }
    //     }
    //   }
    //   if (getUserRoles()) {
    //     // store.state.Roles
    //     // config.headers.Authorization = storage.get(store.state.Roles);
    //   }
    // }
    return config
  },
  (error: any) => {
    console.error(error)
    message.error(error.data.error.message)
    return Promise.reject(error.data.error.message)
  }
)

// function function1() {
//   // 当状态码为20x时进入这里
// }
// function function2() {
//   // 当状态码为非20x时进入这里
// }
// // 响应拦截器
// instance.interceptors.response.use(function1, function2)

// 进入function1后返回的

// 响应拦截器
// :<AxiosResponse<RequestData, any>>
instance.interceptors.response.use(
  (config: AxiosResponse) => {
    removePending(config.config)
    // 请求成功
    if (config.status === 200 || config.status === 204) {
      let result = config.data.result
      let data = config.data.data
      let msg = config.data.msg
      if (config.data == 50000) {
        clearStorage()
        router.push('/')
        return Promise.reject(config)
      }
      if (config.data == 40000) {
        message.success(msg)
        return Promise.reject(config)
      }

      if (result === 'success' && data === null && msg) {
        console.log('接口数据状态 success', config.config.baseURL, config.data)
        // @ts-ignore
        config.config.successMessage && message.success(config.data.msg)
      }

      if (result === 'error' && data === null && msg) {
        console.log('接口数据状态 error', config.config.baseURL, config.data)
        message.error(config.data.msg)
        return Promise.reject(config.data)
      }
      return Promise.resolve(config)
    } else {
      console.log('接口返回状态非200', config.config.baseURL, config)
      return Promise.reject(config)
    }
  },
  // 请求失败
  function (error: any) {
    const { response } = error
    if (axios.isCancel(error)) {
      // 取消请求的错误
      console.log(error.message) // 做出相应的处理
      // 中断 promise 链接
      return Promise.resolve(true)
    } else if (response) {
      // 请求出错了
      errorHandle(response.status, response.data.message)
      //? 响应报错重新请求 -----------------------------------
      // // 超时重新请求
      // const config = error.config;
      // // 全局的请求次数,请求的间隙
      // const [RETRY_COUNT, RETRY_DELAY] = [3, 1000];

      // if (config && RETRY_COUNT) {
      //   // 设置用于跟踪重试计数的变量
      //   config.__retryCount = config.__retryCount || 0;
      //   // 检查是否已经把重试的总数用完
      //   if (config.__retryCount >= RETRY_COUNT) {
      //     return Promise.reject(response || { message: error.message });
      //   }
      //   // 增加重试计数
      //   config.__retryCount++;
      //   // 创造新的Promise来处理指数后退
      //   const backoff = new Promise<void>((resolve) => {
      //     setTimeout(() => {
      //       resolve();
      //     }, RETRY_DELAY || 1);
      //   });
      //   // instance重试请求的Promise
      //   return backoff.then(() => {
      //     return instance(config);
      //   });
      // }
      //? 响应报错重新请求 -----------------------------------

      return Promise.reject(response)
    } else {
      // 处理断网的情况
      // eg:请求超时或断网时，更新state的network状态
      // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
      // 后续增加断网情况下做的一些操作
      // store.commit('networkState', false);
      return Promise.reject(error)
    }
  }
)

// 只需要考虑单一职责，这块只封装axios
// export default instance
export default class Request {
  // static AJAXUrl = process.env.VUE_APP_BASE_API
  /**
   * get方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static get<T>(url: string, params?: any, config?: any) {
    return new Promise<CustomRequest<T>>((resolve, reject) => {
      try {
        // 去除参数内所有非法字符
        if (params) {
          let currentData: any
          checkComplexDataw(params, (data: any, keyList: any) => {
            currentData = params
            for (let i = 0; i < keyList.length - 1; i++) {
              currentData = currentData[keyList[i]]
            }
            if (keyList[keyList.length - 1].toLocaleLowerCase().indexOf('date') === -1)
              currentData[keyList[keyList.length - 1]] = removeStrInvaildSymb(data)
          })
        }

        let defaultConfig = {
          successMessage: true
        }

        if (config) Object.assign(defaultConfig, config)
        instance({
          url,
          method: 'get',
          params,
          ...defaultConfig
        })
          .then((res: any) => {
            console.log('get 请求成功', url, res)
            if (res && res.data) {
              resolve(res.data)
            } else {
              console.error(res)
              reject(res)
            }
          })
          .catch(err => {
            console.error('get 请求失败:' + url, err)
            reject(err)
          })
      } catch (err) {
        console.error('get 请求失败', url, err)
        reject(false)
      }
    })
  }

  /**
   * post方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static post<T>(url: string, params?: any, config?: any) {
    return new Promise<CustomRequest<T>>(async (resolve, reject) => {
      try {
        // 去除参数内所有非法字符
        if (params && Object.prototype.toString.call(params) !== '[object File]') {
          let currentData: any
          checkComplexDataw(params, (data: any, keyList: any) => {
            currentData = params
            for (let i = 0; i < keyList.length - 1; i++) {
              if (Object.prototype.toString.call(currentData[keyList[i]]) === '[object File]')
                continue
              currentData = currentData[keyList[i]]
            }
            currentData[keyList[keyList.length - 1]] = removeStrInvaildSymb(data)
          })
        }

        let defaultConfig = {
          successMessage: true
        }

        if (config) Object.assign(defaultConfig, config)
        console.log(defaultConfig, config)

        instance({
          url,
          method: 'post',
          data: params,
          ...defaultConfig
        })
          .then((res: AxiosResponse) => {
            console.log('post 请求成功', url, res)
            if (res && res.data) {
              resolve(res.data)
            } else {
              reject(res)
            }
          })
          .catch(err => {
            console.error('post 请求失败', url, err)
            reject(err)
          })
      } catch (err) {
        console.error('post 请求失败', url, err)
        reject(false)
      }
    })
  }
  /**
   * 获取文件 post方法
   * @param {string} url 路径
   * @param {any} params 参数
   * @param {any} config 其他配置
   */
  static filePost(url: string, params?: any, config?: any) {
    return new Promise<Blob>(async (resolve, reject) => {
      try {
        // 去除参数内所有非法字符
        if (params && Object.prototype.toString.call(params) !== '[object File]') {
          let currentData: any
          checkComplexDataw(params, (data: any, keyList: any) => {
            currentData = params
            for (let i = 0; i < keyList.length - 1; i++) {
              if (Object.prototype.toString.call(currentData[keyList[i]]) === '[object File]')
                return
              currentData = currentData[keyList[i]]
            }
            currentData[keyList[keyList.length - 1]] = removeStrInvaildSymb(data)
          })
        }

        let defaultConfig = {
          successMessage: true
        }
        if (config) Object.assign(defaultConfig, config)

        instance({
          url,
          method: 'post',
          data: params,
          ...defaultConfig
        })
          .then((res: AxiosResponse) => {
            console.log('post 请求成功', url, res)
            if (res && res.data) {
              resolve(res.data)
            } else {
              reject(!res ? false : !res.data ? true : false)
            }
          })
          .catch((err: any) => {
            console.error('post 请求失败', url, err)
            reject(false)
          })
      } catch (err) {
        console.error('post 请求失败', url, err)
        reject(false)
      }
    })
  }
}
